From d5b6445d6587473f9bb669fa7117c2621408b8ca Mon Sep 17 00:00:00 2001
From: Fuwn <contact@fuwn.me>
Date: Fri, 29 Dec 2023 22:17:10 -0800
Subject: feat(badges): live update on delete and add

---
 src/routes/user/[user]/badges/+page.svelte | 180 +++++++++++++++++------------
 1 file changed, 105 insertions(+), 75 deletions(-)

(limited to 'src/routes/user/[user]/badges/+page.svelte')

diff --git a/src/routes/user/[user]/badges/+page.svelte b/src/routes/user/[user]/badges/+page.svelte
index febf5bdf..8a15425c 100644
--- a/src/routes/user/[user]/badges/+page.svelte
+++ b/src/routes/user/[user]/badges/+page.svelte
@@ -14,7 +14,7 @@
 	let currentUserIdentity: ReturnType<typeof userIdentity>;
 	let error: null | string;
 	// const socket = io();
-	let badges: Badge[] | null = null;
+	let badgesPromise: Promise<Response>;
 	let dark = true;
 	let transparent = false;
 	let confirmDelete = 0;
@@ -22,7 +22,7 @@
 	onMount(async () => {
 		// socket.on('badges', (message) => (badges = message));
 
-		badges = await (await fetch(`/api/badges?id=${(await user(data.username)).id}`)).json();
+		badgesPromise = fetch(`/api/badges?id=${(await user(data.username)).id}`);
 
 		if (data.user) {
 			currentUserIdentity = userIdentity(data.user);
@@ -62,12 +62,16 @@
 			{
 				method: 'PUT'
 			}
-		).then(() => {
-			error = null;
-			imageURL.value = '';
-			activityURL.value = '';
-			description.value = '';
-		});
+		)
+			.then(() => {
+				error = null;
+				imageURL.value = '';
+				activityURL.value = '';
+				description.value = '';
+			})
+			.then(async () => {
+				badgesPromise = fetch(`/api/badges?id=${(await user(data.username)).id}`);
+			});
 	};
 
 	const removeBadge = (badge: Badge) => {
@@ -87,9 +91,13 @@
 
 		fetch(`/api/badges?id=${badge.id}`, {
 			method: 'DELETE'
-		}).then(() => {
-			(document.querySelector(`#badge-${badge.id}`) as HTMLAnchorElement).style.display = 'none';
-		});
+		})
+			.then(() => {
+				(document.querySelector(`#badge-${badge.id}`) as HTMLAnchorElement).style.display = 'none';
+			})
+			.then(async () => {
+				badgesPromise = fetch(`/api/badges?id=${(await user(data.username)).id}`);
+			});
 	};
 
 	const screenshot = async () => {
@@ -148,74 +156,96 @@
 <HeadTitle route={`${data.username}'s Badge Wall`} path={`/user/${data.username}`} />
 
 {#await currentUserIdentity}
-	Loading user identity ... 50%
+	Loading user identity ... 60%
 {:then identity}
-	{@const isOwner = identity && identity.name === data.username}
-
-	{#if isOwner}
-		<p>
-			<a href={`/user/${data.username}`}>Back to Profile</a>
-			•
-			<a href={`#`} on:click={() => (editMode = !editMode)}>
-				{editMode ? 'Disable' : 'Enable'} Edit Mode
-			</a>
-			•
-			<a href={`#`} on:click={() => screenshot()}>Download</a>
-			•
-			<input type="checkbox" bind:checked={dark} /> Dark Mode
-			<input type="checkbox" bind:checked={transparent} /> Transparent Background
-		</p>
-	{/if}
-
-	{#if editMode && isOwner}
-		<p>
-			Edit mode is enabled. Click on an image <b>twice</b> to delete it. There is no confirmation, so
-			be careful!
-		</p>
-
-		{#if error}
-			<p style="color: red;">{error}</p>
-		{/if}
+	{#await badgesPromise}
+		Loading badges ... 40%
+	{:then badgesResponse}
+		{#if badgesResponse}
+			{#await badgesResponse.json()}
+				Loading badges ... 80%
+			{:then badges}
+				{@const isOwner = identity && identity.name === data.username}
+
+				{#if isOwner}
+					<p>
+						<a href={`/user/${data.username}`}>Back to Profile</a>
+						•
+						<a href={`#`} on:click={() => (editMode = !editMode)}>
+							{editMode ? 'Disable' : 'Enable'} Edit Mode
+						</a>
+						•
+						<a href={`#`} on:click={() => screenshot()}>Download</a>
+						•
+						<input type="checkbox" bind:checked={dark} /> Dark Mode
+						<input type="checkbox" bind:checked={transparent} /> Transparent Background
+					</p>
+				{/if}
 
-		<p>
-			<input type="text" placeholder="Image URL" name="image_url" minlength="1" maxlength="1000" />
-			<input
-				type="text"
-				placeholder="Activity URL"
-				name="activity_url"
-				minlength="1"
-				maxlength="1000"
-			/>
-			<input
-				type="text"
-				placeholder="Description (Optional)"
-				name="description"
-				minlength="1"
-				maxlength="1000"
-			/>
-			<a href={`#`} on:click={submitBadge}>Add Badge</a>
-		</p>
-	{/if}
-
-	<div id="badges">
-		{#if badges === null}
-			{@html nbsp('Loading badges ... 50%')}
-		{:else if badges.length === 0}
-			{@html nbsp('No badges found for this user.')}
-		{:else}
-			{#each badges as badge}
-				{#if editMode}
-					<a href={`#`} on:click={() => removeBadge(badge)} id={`badge-${badge.id}`}>
-						<img src={badge.image} alt={badge.description} title={badge.description} />
-					</a>
-				{:else}
-					<a href={badge.post} target="_blank" id={`badge-${badge.id}`}>
-						<img src={badge.image} alt={badge.description} title={badge.description} />
-					</a>
+				{#if editMode && isOwner}
+					<p>
+						Edit mode is enabled. Click on an image <b>twice</b> to delete it. There is no confirmation,
+						so be careful!
+					</p>
+
+					{#if error}
+						<p style="color: red;">{error}</p>
+					{/if}
+
+					<p>
+						<input
+							type="text"
+							placeholder="Image URL"
+							name="image_url"
+							minlength="1"
+							maxlength="1000"
+						/>
+						<input
+							type="text"
+							placeholder="Activity URL"
+							name="activity_url"
+							minlength="1"
+							maxlength="1000"
+						/>
+						<input
+							type="text"
+							placeholder="Description (Optional)"
+							name="description"
+							minlength="1"
+							maxlength="1000"
+						/>
+						<a href={`#`} on:click={submitBadge}>Add Badge</a>
+					</p>
 				{/if}
-			{/each}
+
+				<div id="badges">
+					{#if badges === null}
+						{@html nbsp('Loading badges ... 50%')}
+					{:else if badges.length === 0}
+						{@html nbsp('No badges found for this user.')}
+					{:else}
+						{#each badges as badge}
+							{#if editMode}
+								<a href={`#`} on:click={() => removeBadge(badge)} id={`badge-${badge.id}`}>
+									<img src={badge.image} alt={badge.description} title={badge.description} />
+								</a>
+							{:else}
+								<a href={badge.post} target="_blank" id={`badge-${badge.id}`}>
+									<img src={badge.image} alt={badge.description} title={badge.description} />
+								</a>
+							{/if}
+						{/each}
+					{/if}
+				</div>
+			{:catch}
+				<p>Could not parse badges</p>
+			{/await}
+		{:else}
+			Loading badges ... 20%
 		{/if}
-	</div>
+	{:catch}
+		<p>Could not fetch badges</p>
+	{/await}
 {/await}
 
 <style>
-- 
cgit v1.2.3

